<!doctype html>

<title>12 Component Refactor - React From Zero</title>

<script src="https://unpkg.com/react@16.4.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.4.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/create-react-class@15.6.3/create-react-class.js"></script>
<script src="https://unpkg.com/prop-types@15.6.1/prop-types.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<div id="app"></div>

<script type="text/babel">
// Refactoring is another thing that is nice with React
// First we'll talk about refactoring one component into another
// if we're lucky, we can just change the components implementation
// and don't need to change anything at the call-site

// We start with a component that renders records somehow
function ViewBefore(props) {
  return (
    <table>
      <thead>
        <tr>
          <th>Room</th>
          <th>People</th>
        </tr>
      </thead>
      <tbody>
        {props.rooms.map(function(room, k) {
          return (
            <tr key={k}>
              <td>{room.name}</td>
              <td>{room.people}</td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

// The component has a simple props-interface
ViewBefore.propTypes = {
  rooms: PropTypes.arrayOf(PropTypes.shape({
    name: PropTypes.string.isRequired,
    people: PropTypes.number.isRequired
  })).isRequired
}

// We switch out the implementation with something more complex
function ViewAfter(props) {
  return (
    <div>
      {props.rooms.map(function(room, k) {
        var barStyle = {
          display: "inline-block",
          background: "lightgrey",
          width: room.people * 25
        };
        return (
          <div key={k}>
            {room.people > 0 ? (
              <span style={barStyle}>{room.people} People</span>
            ) : (
              <span>0 People</span>
            )}
            <span> in {room.name}</span>
          </div>
        );
      })}
    </div>
  );
}
// We keep the props-interface the same
ViewAfter.propTypes = {
  rooms: PropTypes.arrayOf(PropTypes.shape({
    name: PropTypes.string.isRequired,
    people: PropTypes.number.isRequired
  })).isRequired
}

// We could also switch it with something more dynamic
var ViewDynamic = createReactClass({
  // We still keep the props-interface the same
  propTypes: {
    rooms: PropTypes.arrayOf(PropTypes.shape({
      name: PropTypes.string.isRequired,
      people: PropTypes.number.isRequired
    })).isRequired
  },

  getInitialState: function() {
    return { currentRoom: 0 };
  },

  componentDidMount() {
    var component = this;
    var props = this.props;

    this.interval = setInterval(function() {
      var currentRoom =
        component.state.currentRoom < props.rooms.length - 1
          ? component.state.currentRoom + 1
          : 0;
      component.setState({ currentRoom: currentRoom });
    }, 1000);
  },

  componentWillUnmount() {
    clearInterval(this.interval);
  },

  render: function() {
    var room = this.props.rooms[this.state.currentRoom];

    return (
      <span style={{ color: this.state.color }}>
        Room <b>{room.name}</b> has <b>{room.people}</b> People.
      </span>
    );
  }
});

// Some data
var rooms = [
  { name: "Office", people: 10 },
  { name: "Kitchen", people: 15 },
  { name: "Floor", people: 3 },
  { name: "Bathroom", people: 0 }
];

// As we can see the components can be used exactly the same
// If we copy the implementation of ViewAfter into ViewBefore,
// everything keeps working
var reactElement = (
  <div style={{ margin: "auto", width: 500 }}>
    <h3>Before the refactor</h3>
    <ViewBefore rooms={rooms} />

    <h3>After the refactor</h3>
    <ViewAfter rooms={rooms} />

    <h3>Dynamic refactor</h3>
    <ViewDynamic rooms={rooms} />
  </div>
);

ReactDOM.render(reactElement, document.getElementById("app"));
</script>